import store from './store'
import CryptoJS from 'cryptojs'
import {Message} from 'element-ui'

function toQueryString (obj) {
    if (obj === null) {
        return ''
    }
    if (typeof obj === 'function') {
        return toQueryString(obj())
    }
    let kv = []
    // console.log(obj.page);
    for (let key of Object.keys(obj)) {
        let val = obj[key]
        if (val instanceof Array) {
            for (let v of val) {
                kv.push(encodeURIComponent(key) + '=' + encodeURIComponent(valueOf(v)))
            }
        } else {
            kv.push(encodeURIComponent(key) + '=' + encodeURIComponent(valueOf(val)))
        }
    }
    return kv.join('&')
}

function valueOf (obj) {
    if (obj === null || typeof obj === 'undefined') {
        return ''
    }
    if (typeof obj === 'function') {
        return valueOf(obj())
    }
    return obj.toString()
}

function error (status, statusText, response) {
    if (status <= 199) {
        console.info(statusText)
    } else if (status >= 200 && status <= 299) {
        console.info(statusText)
    } else if (status >= 300 && status <= 399) {
        if (typeof response === 'string') {
            location.href = response
        } else if (response && response.redirectUrl) {
            location.href = response.redirectUrl
        } else {
            location.href = '/'
        }
    } else if (status >= 400 && status <= 499) {
        switch (status) {
        case 401:
            location.href = store.state.page.login + '?redirect=' + encodeURIComponent(location.href)
            break
        case 403:
            location.href = store.state.page.login
            break
        default:
            // eslint-disable-next-line no-case-declarations
            let e = new Error(statusText)
            e.response = response
            throw e
        }
    } else if (status >= 500 && status <= 599) {
        Message.warning('Request Error due to ' + (statusText || 'Server Error'))
    } else {
        Message.warning('Request Error' + (statusText ? ' due to ' + statusText : '') + '.')
    }
}

function getJson (url, params, complete) {
    let queryString = toQueryString(params)
    if (queryString.length > 0) {
        url = url + (url.indexOf('?') > 0 ? '' : '?') + queryString
    }

    let options = {
        method: 'GET',
        cache: 'no-cache',
        mode: store.state.cors ? 'cors' : 'same-origin',
        credentials: store.state.cors ? 'include' : 'same-origin',
        headers: {
            'Accept': 'application/json'
        }
    }

    return _fetchJson(url, options, complete)
}

function get (url, params, complete) {
    let queryString = toQueryString(params)
    if (queryString.length > 0) {
        url = url + (url.indexOf('?') > 0 ? '' : '?') + queryString
    }

    let options = {
        method: 'GET',
        cache: 'no-cache',
        mode: store.state.cors ? 'cors' : 'same-origin',
        credentials: store.state.cors ? 'include' : 'same-origin',
        headers: {}
    }
    return _fetch(url, options, complete)
}

function postJson (url, params, complete) {
    if (store.state.isLocal) {
        return getJson(url, params, complete)
    }
    params = params || {}
    let data = new FormData()
    for (let key of Object.keys(params)) {
        data.append(key, params[key])
    }
    let options = {
        method: 'POST',
        cache: 'no-cache',
        mode: store.state.cors ? 'cors' : 'same-origin',
        credentials: store.state.cors ? 'include' : 'same-origin',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept': 'application/json'
        },
        body: toQueryString(params)
    }
    return _fetchJson(url, options, complete)
}

function post (url, params, complete) {
    params = params || {}

    let data = new FormData()
    for (let key of Object.keys(params)) {
        data.append(key, params[key])
    }

    let options = {
        method: 'POST',
        cache: 'no-cache',
        mode: store.state.cors ? 'cors' : 'same-origin',
        credentials: store.state.cors ? 'include' : 'same-origin',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: toQueryString(params)
    }
    return _fetch(url, options, complete)
}

function _fetchJson (url, options, complete) {
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = store.state.apiPath + url
    }
    return new Promise(function (resolve, reject) {
        fetch(url, options).then(function (response) {
            if (response.ok) {
                return response
            } else {
                error(response.status, response.statusText, response)
            }
        }, function (e) {
            let error = new Error(e)
            error.fetchFailed = true
            throw error
        }).then(response => response.json()).then(data => {
            resolve(data)
            typeof complete === 'function' && complete()
        }).catch(function (e) {
            if (e.fetchFailed) {
                let data = {
                    message: '请求失败',
                    response: e.response
                }
                reject(data)
            } else if (e instanceof SyntaxError) {
                let data = {
                    message: '结果格式错误',
                    error: e
                }
                reject(data)
            } else if (e.response) {
                e.response.json().then(data => reject(data))
            } else {
                let data = {
                    message: '未知异常',
                    error: e
                }
                reject(data)
            }
            typeof complete === 'function' && complete()
        })
    })
}

function _fetch (url, options, complete) {
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = store.state.apiPath + url
    }
    return new Promise(function (resolve, reject) {
        fetch(url, options).then(function (response) {
            if (response.ok) {
                return response
            } else {
                error(response.status, response.statusText, response)
            }
        }, function (e) {
            let error = new Error(e)
            error.fetchFailed = true
            throw error
        }).then(response => {
            resolve(response.text())
            typeof complete === 'function' && complete()
        }).catch(function (e) {
            reject(e.response)
            typeof complete === 'function' && complete()
        })
    })
}

function encode (message, key) {
    return CryptoJS.AES.encrypt(message, CryptoJS.enc.Hex.parse(key), {
        iv: CryptoJS.enc.Latin1.parse('Pkcs7'),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    }).toString().replace(/\+/g, '-').replace(/\//g, '.').replace(/=/g, '~')
}

function decode (message, key) {
    return CryptoJS.AES.decrypt(message.replace(/-/g, '+').replace(/\./g, '/').replace(/~/g, '='), CryptoJS.enc.Hex.parse(key), {
        iv: CryptoJS.enc.Latin1.parse('Pkcs7'),
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
    }).toString()
}

function md5 (obj) {
    if (typeof obj === 'string') {
        return CryptoJS.MD5(obj).toString()
    } else {
        return CryptoJS.MD5(JSON.stringify(obj)).toString()
    }
}

function isEmpty (obj, fields = []) {
    if (fields.length) {
        for (let key of fields) {
            if (!obj[key]) {
                return true
            }
        }
    } else {
        for (let val of obj.values()) {
            if (!val) {
                return true
            }
        }
    }
    return false
}

function decodeUri (str) {
    return str === null || str === undefined ? '' : unescape(str.replace(/\\u([0-9A-Fa-f]{4})/g, '%u$1'))
}

function labelRender (h, {column}) {
    let rows = []
    column.label.split('\n').forEach((label, index) => {
        if (index !== 0) {
            rows.push(h('br'))
        }
        rows.push(h('span', {}, label))
    })
    return h('span', {}, rows)
}

function rangeOfToday () {
    const milli = 3600 * 1000 * 24
    const end = new Date()
    const start = new Date()
    start.setTime(start.getTime() - (start.getTime() % milli) + start.getTimezoneOffset() * 60000)
    end.setTime(end.getTime() - (end.getTime() % milli) + milli - 1 + end.getTimezoneOffset() * 60000)
    return [start, end]
}

function datetimeFormat (date) {
    function fill (num) {
        return num >= 10 ? num : '0' + num
    }

    let str = date.getFullYear() + '-' + fill(date.getMonth() + 1) + '-' + fill(date.getDate())
    str += ' ' + fill(date.getHours()) + ':' + fill(date.getMinutes()) + ':' + fill(date.getSeconds())
    return str
}

function dateFormat (date) {
    function fill (num) {
        return num >= 10 ? num : '0' + num
    }

    return date.getFullYear() + '-' + fill(date.getMonth() + 1) + '-' + fill(date.getDate())
}

const methods = {
    get,
    getJson,
    post,
    postJson,
    encode,
    decode,
    md5,
    isEmpty,
    decodeUri,
    labelRender,
    rangeOfToday,
    dateFormat,
    datetimeFormat
}

let Utils = {}
// eslint-disable-next-line no-unused-vars
Utils.install = function (Vue, options) {
    // 1. 添加全局方法或属性
    Vue._GlobalMethod = function () {
        // 逻辑...
    }

    Vue.$utils = methods

    // // 2. 添加全局资源
    // Vue.directive('_myDirective', {
    //     bind (el, binding, vnode, oldVnode) {
    //         // 逻辑...
    //     }
    // })
    //
    // // 3. 注入组件选项
    // Vue.mixin({
    //     created: function () {
    //         // 逻辑...
    //     }
    // })

    // 4. 添加实例方法
    Vue.prototype.utils = methods
}
export default Utils
